/*
 * Copyright (C) 2013 by KB3ZYT Richard Nash
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
 
#include "BalloonStateMachine.h"
#include <SD.h>
#include <DHT.h>
#ifdef INCLUDE_BMP
#include <BMP085.h>
#endif
#include <APRS_HEADERS.h>


#define DHTPIN 4     // what pin the DHT module is connected to
#define DHTTYPE DHT22   // DHT 22  (AM2302)
DHT dht(DHTPIN, DHTTYPE);
#ifdef INCLUDE_BMP
BMP085 bmp;
#endif

BalloonStateMachine::BalloonStateMachine()
{
    state = STATE_INIT;
    tmpCounter = 0;
    dht.begin();
#ifdef INCLUDE_BMP
    bmp.begin();
#endif
}


void BalloonStateMachine::initializeLogger(GPS &gps)
{
    logFileName[0] = '0' + (gps.year / 10);
    logFileName[1] = '0' + (gps.year % 10);
    logFileName[2] = '0' + (gps.month / 10);
    logFileName[3] = '0' + (gps.month % 10);
    logFileName[4] = '0' + (gps.day / 10);
    logFileName[5] = '0' + (gps.day % 10);
    logFileName[6] = '.';
    logFileName[7] = 't';
    logFileName[8] = 'x';
    logFileName[9] = 't';
    logFileName[10] = 0;
}

void BalloonStateMachine::logLocation(GPS &gps,
                                      float outsideTemperature,
                                      float outsideHumidity,
                                      float insideTemperature,
                                      float barametricPressure)
{
    // open the file. note that only one file can be open at a time,
    // so you have to close this one before opening another.
    File dataFile = SD.open(logFileName, FILE_WRITE);
    
    // if the file is available, write to it:
    if (dataFile) {
        dataFile.print(gps.year);
        dataFile.print(',');
        dataFile.print(gps.month);
        dataFile.print(',');
        dataFile.print(gps.day);
        dataFile.print(',');
        dataFile.print(gps.hour);
        dataFile.print(',');
        dataFile.print(gps.minute);
        dataFile.print(',');
        dataFile.print(gps.seconds);
        dataFile.print(',');
        dataFile.print(state);
        dataFile.print(',');
        dataFile.print(gps.latitude,7);
        dataFile.print(',');
        dataFile.print(gps.longitude,7);
        dataFile.print(',');
        dataFile.print(gps.altitude,0);
        dataFile.print(',');
        dataFile.print(gps.fix);
        dataFile.print(',');
        dataFile.print(gps.fixquality);
        dataFile.print(',');
        dataFile.print(gps.satellites);
        dataFile.print(',');
        dataFile.print(outsideTemperature,1);
        dataFile.print(',');
        dataFile.print(outsideHumidity,1);
        dataFile.print(',');
        dataFile.print(insideTemperature,1);
        dataFile.print(',');
        dataFile.print(barametricPressure,1);
        dataFile.println("");
        dataFile.close();
    }  
}

void BalloonStateMachine::initializeRadio()
{
    afsk_setup();
    lastTransmitionTime = 0;
}

#define MIN_TIME_BETWEEN_APRS 120000

void BalloonStateMachine::broadcastLocation(GPS &gps,
                                            float outsideTemperature,
                                            float outsideHumidity,
                                            float insideTemperature,
                                            float barametricPressure)
{
    unsigned long now = millis();
    if (lastTransmitionTime == 0 || now - lastTransmitionTime >= MIN_TIME_BETWEEN_APRS) { // Wait at least N/1000 seconds between transmitions
        Serial.println(F("APRS"));
        aprs_send(gps.day, gps.hour, gps.minute
                  ,gps.latitude, gps.longitude // degrees
                  ,gps.altitude // meters
                  ,0
                  ,0
                  ,outsideTemperature, outsideHumidity
                  ,insideTemperature, barametricPressure
                  ,state);
        lastTransmitionTime = now;
    }
}

void BalloonStateMachine::pumpState(GPS &gps)
{
    float dhtHumidity = dht.readHumidity();
    float dhtTemperature = dht.readTemperature();
    
#ifdef INCLUDE_BMP
    float bmpTemperature = bmp.readTemperature();
    float bmpPressure = bmp.readPressure();
    float bmpAltitude = bmp.readAltitude(101500); // Need to fill this in with sea level pressure
#else
    float bmpTemperature=0;
    float bmpPressure=0;
    float bmpAltitude=0;
#endif
    
#ifdef PRINT_TELEMETRY
    Serial.print(F("State: "));
    Serial.println(state);
    Serial.print(F("Pos: "));
    Serial.print(gps.latitude,7);
    Serial.print(F(","));
    Serial.print(gps.longitude,7);
    Serial.print(F(","));
    Serial.println(gps.altitude,0);
    Serial.print(F("Fix: "));
    Serial.print(gps.fix);
    Serial.print(F(" Fix quality:"));
    Serial.print(gps.fixquality);
    Serial.print(F(" satelites:"));
    Serial.println(gps.satellites);
    Serial.print(gps.year);
    Serial.print(F("/"));
    Serial.print(gps.month);
    Serial.print(F("/"));
    Serial.print(gps.day);
    Serial.print(F(" "));
    Serial.print(gps.hour);
    Serial.print(F(":"));
    Serial.print(gps.minute);
    Serial.print(F(":"));
    Serial.println(gps.seconds);
    Serial.print(F("Outside temp, humidity: "));
    Serial.print(dhtTemperature,1);
    Serial.print(F(","));
    Serial.println(dhtHumidity,1);
#endif
    
    if (state != STATE_INIT) {
        logLocation(gps, dhtTemperature, dhtHumidity,bmpTemperature, bmpPressure);
        broadcastLocation(gps,dhtTemperature, dhtHumidity,bmpTemperature, bmpPressure);
    }
    if (state == STATE_INIT) {
            // Just powered up, waiting for GPS fix and then get everything ready
            if (gps.year > 0 && gps.latitude != 0.0 && gps.altitude != 0.0 && gps.fixquality >= 1) {
                // After we get GOOD_FIX_COUNT_TO_READY_STATE quality reports, transition to GPS state
                tmpCounter++;
                if (tmpCounter >= GOOD_FIX_COUNT_TO_READY_STATE) {
                    // Transition to GPS state
                    initializeLogger(gps);
                    initializeRadio();
                    lastTransitionTime = millis();
                    startingAltitude = gps.altitude;
                    state = STATE_AWAITING_LAUNCH;
                }
            }
    } else       
    if (state == STATE_AWAITING_LAUNCH) {
            // GPS acquired ... logging position, waiting to see elevation start to rise
            if (gps.altitude > startingAltitude + 20) {
                // We've climbed more than 60 feet, we are launched and flying baby!
                lastTransitionTime = millis();
                highestAltitude = gps.altitude;
                state = STATE_LAUNCHED;
            }
    } else       
    if (state == STATE_LAUNCHED) {
            // Rising, waiting to see elevation start to drop
            if (gps.altitude < highestAltitude - 100) {
                // We've fallen more than 300 feet
                lastTransitionTime = millis();
                highestAltitude = gps.altitude;
                state = STATE_DESCENT;
            }
            if (gps.altitude > highestAltitude) highestAltitude = gps.altitude;
    } else       
    if (state == STATE_DESCENT) {
            // Balloon falling, waiting till descent stops
    } else       
    if (state == STATE_LANDED) {
    } else       
    if (state == STATE_WAITING) {
    }    
}






